package Algoritmos;
import java.util.*;
import java.util.Map.Entry;

import estructuras.common.*;

//todos los intervalos abiertos por la derecha

public abstract class Solver {
	//dia 0 es lunes
	protected Fecha fechaInicialSimulacion = null;
	protected Fecha fechaFinSimulacion = null;
	protected Fecha fechaInicioUniverso = null;
	protected Vector<Almacen> almacenes = new Vector<Almacen>();
	protected Vector<Conexion> conexiones = new Vector<Conexion>();
	protected Vector<Vuelo> vuelos = new Vector<Vuelo>();
	protected Vector<Paquete> paquetesEstimados = new Vector<Paquete>();
	protected Vector<Paquete> paquetesFijos = new Vector<Paquete>();
	protected Vector<Continente> continentes = new Vector<Continente>();
	protected LinkedList<Paquete> paquetesSinRuta = new LinkedList<>();
	private String nombre = "ninguno";
	protected HashMap<Entry<Almacen, Almacen>, Distribucion> distribuciones = new HashMap<>();
	protected Solver(String nombre){
		this.nombre = nombre;
	}
	public String getNombre(){
		return nombre;
	}
	
	static Fecha duracionVueloIntracontinental = new Fecha(0, 12);
	static Fecha duracoinVueloIntercontinental = new Fecha(1, 0);
	
	static public Fecha getDuracionVueloIntracontinental(){
		return duracionVueloIntracontinental;
	}
	
	static public Fecha getDuracionVueloIntercontinental(){
		return duracoinVueloIntercontinental;
	}
	
	public void setFechaInicialSimulacion(Fecha fecha){
		fechaInicialSimulacion = fecha;
	}
	
	public void setFechaFinSimulacion(Fecha fecha){
		fechaFinSimulacion = fecha;
	}
	
	public void setFechaInicioUniverso(Fecha fecha){
		fechaInicioUniverso = fecha;
	}
	
	public Continente addContinente(String nombre){
		Continente continente = new Continente(nombre);
		continentes.add(continente);
		return continente;
	}
	public Almacen addAlmacen(String nombre, int capacidadInicial, Continente continente){
		if(fechaInicialSimulacion == null)
			return null;
		Almacen almacen = new Almacen(nombre, continente);
		almacenes.add(almacen);
		almacen.addCambioDeCapacidad(capacidadInicial, fechaInicialSimulacion);
		continente.addAlmacen(almacen);
		return almacen;
	}
	
	
	public Conexion addConexion(Almacen origen, Almacen destino, int hora, boolean[] frecuencia, int capacidadInicial){
		Conexion conexion = new Conexion(origen, destino, hora, frecuencia, capacidadInicial);
		conexiones.add(conexion);
		return conexion;
	}
	
	//los intervalos son abiertos en la derecha
	protected List<Vuelo> generaVuelosEnIntervaloDeTiempo(Fecha fechaInicioIntervalo, int horasIntervalo){
		Vector<Vuelo> agregados = new Vector<Vuelo>();
		for(Conexion c : conexiones)
			for(int i = 0; i < 7; i++) if(c.ocurreEnDia(i)){
				Fecha primeraFecha = Fecha.getPrimeraFecha(fechaInicialSimulacion, i, c.getHoraDeVuelo());
				if(primeraFecha.compareTo(fechaInicialSimulacion) < 0) continue;
				while(primeraFecha.toHoras() - fechaInicioIntervalo.toHoras() < horasIntervalo){
					Vuelo vuelo = new Vuelo( new Fecha( primeraFecha.getDia() , primeraFecha.getHora() ), c);
					this.vuelos.add(vuelo);
					agregados.add(vuelo);
					primeraFecha.addHoras(7 * 24);
					c.getOrigen().addVuelo(vuelo);
				}
			}
		return agregados;
	}
	
	void addPaqueteGenerico(Paquete paq){
		paq.getOrigen().addPaqueteOriginadoEnAlmacen(paq);
	}
	protected void addPaqueteFijo(Paquete paq){
		this.paquetesFijos.add(paq);
		addPaqueteGenerico(paq);
	}
	protected void addPaqueteEstimado(Paquete paq){
		this.paquetesEstimados.add(paq);
		addPaqueteGenerico(paq);
	}
	
	public abstract List<Paquete> generaPaquetesEstimadosParaIntervaloDeTiempo(Fecha fechaInicio, Fecha fechaFin);
	
	protected abstract Ruta generaRutaDePaquete(Paquete paquete);

	public List<Continente> getContinentes() {
		return this.continentes;
	}

	public Continente getContinentePorNombre(String nombre) {
		for(Continente c : continentes)
			if(c.getNombre().equals(nombre)) return c;
		return null;
	}

	public Almacen getAlmacenPorNombre(String nombre) {
		for(Almacen a : almacenes) 
			if(a.getNombre().equals(nombre)) return a;
		return null;
	}
	
	public abstract void generaYEjecutaRutasParaIntervaloDeTiempo(Fecha fechaInicio, Fecha fechaFin);
	
	protected Vector<Paquete> getPaquetesEnIntervaloDeTiempo(Fecha fechaInicio, Fecha fechaFin){
		Paquete fakeInicio = new Paquete(null, null, fechaInicio, true);
		Paquete fakeFin = new Paquete(null, null, fechaFin, true);
		Vector< Paquete > ans = new Vector<>();
		for( Paquete p : paquetesEstimados ) if( fechaInicio.compareTo( p.getFechaRegistro() ) <= 0 &&
												 fechaFin.compareTo( p.getFechaRegistro() ) >= 0 ){
			ans.add( p );
		}
		for( Paquete p : paquetesFijos ) if( fechaInicio.compareTo( p.getFechaRegistro() ) <= 0 &&
												fechaFin.compareTo( p.getFechaRegistro() ) >= 0 ){
			ans.add( p );
		}
	//	SortedSet<Paquete> estimados = this.paquetesEstimados.subSet(fakeInicio, true, fakeFin, false);
	//	SortedSet<Paquete> fijos = this.paquetesFijos.subSet(fakeInicio, true, fakeFin, false);
	//	estimados.addAll(fijos);
		return ans;
	}
	
	protected void addPaqueteSinRuta(Paquete p){
		this.paquetesSinRuta.add(p);
	}
	
	@SuppressWarnings("unchecked")
	public List<Paquete> getPaquetesSinRuta(){
		return (List<Paquete>)this.paquetesSinRuta.clone();
	}
	public List<Almacen> getAlmacenes() {
		return this.almacenes;
	}
	public abstract void addDistribucion(Almacen origen, Almacen destino, int media,
			double varianza);

}
